 using System;
using System.Globalization;
using Postcode.Settings;
using System.Text.RegularExpressions;

namespace Postcode
{
    public class Postcode : IDisposable
    {
        private string _code;
        private PostcodeProperty _properties;

        /// <summary>
        /// The region this postcode should match to
        /// </summary>
        public PostcodeProperty Properties 
        {
            get { return this._properties; }
        }
        
        /// <summary>
        /// Constructor for the postcode object
        /// </summary>
        /// <param name="Pc">String representation of the postcode</param>
        /// <param name="Region">Region we want to match the postcode agains</param>
        public Postcode(string Pc, RegionInfo Region)
        {
            // make sure both values are not empty
            if (String.IsNullOrEmpty(Pc)) throw new Exception("No postcode given");
            if (Region == null) throw new Exception("No postcode region given");
            
            this._code = Pc;
            this._properties = new PostcodeProperties().Get(Region);
        }
        
        /// <summary>
        /// Returns the postcode equivalent to the postcode
        /// </summary>
        /// <returns>String representation of the postcode</returns>
        public override string ToString()
        {
            return this.ToString(false);
        }
        /// <summary>
        /// Returns the value of the postcode, if strict is wanted it transforms the postcode to strict
        /// </summary>
        /// <param name="Strict">Do we want a strict representation?</param>
        /// <returns>String representation of the current postcode</returns>
        public string ToString(bool Strict)
        {
            return (Strict) ? this.toStrict() : this._code;
        }
        
        /// <summary>
        /// Validates the current postcode as loosely valid
        /// </summary>
        /// <returns>Is the postcode loosely valid?</returns>
        public bool IsValid()
        {
            return this.IsValid(false);
        }
        /// <summary>
        /// Checks whether the given postcode is valid
        /// </summary>
        /// <param name="Strict">Do we want to match it as strict or loose</param>
        /// <returns>whether postcode is valid</returns>
        public bool IsValid(bool Strict)
        {
            return (Strict) ? this._properties.Strict.IsMatch(this._code) : this._properties.Loose.IsMatch(this._code);
        }
        
        /// <summary>
        /// Transforms the current postcode to a Strict formatted version if it isn't already a strict format
        /// </summary>
        /// <returns>Strict representation of the given input</returns>
        private string toStrict()
        {
            if (this.IsValid(true)) 
            {   // if the code is strict valid, return the value
                return this._code;
            }
            
            // if the code is not strict valid we remove all spaces and capitalize any letter
            string tmp = this._code.Replace(" ", "").ToUpper();
            
            // next we need to add spaces to make it match the strict format
            // use all the regular expression in the PostcodeParts to create a strict formatted code
            try
            {
                foreach (string pattern in this._properties.PostcodeParts)
                {
                    Match m = Regex.Match(tmp, pattern);
                    tmp = (m.Success) ? Regex.Replace(tmp, pattern, " " + m.Groups[0].Value + " ") : tmp;

                    //always remove duplicate spaces to make regular expressions work for more parts
                    tmp = tmp.Replace("  ", " ");
                }
            }
            catch
            {   
                // if regular expressions fail just return the regular code
                return this._code;
            }
            return tmp.Trim().Replace("  ", " ");
        }

        /// <summary>
        /// Doesn't do a thing but is only included for the use of using, for those
        /// that like to program using the using statement
        /// </summary>
        public void Dispose() { }
    }
}
